package com.xdxc.gateway.config;


import com.xdxc.utils.JWTUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.security.SignatureException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;


@Component
@Slf4j
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {

    private final ReactiveRedisTemplate<String, String> reactiveRedisTemplate;


    @Value("${security.jwt.token-prefix:Bearer}")
    private String tokenPrefix;

    @Value("${security.jwt.enable-redis-check:true}")
    private boolean enableRedisCheck;

    @Autowired
    public JwtAuthenticationFilter(ReactiveRedisTemplate<String, String> reactiveRedisTemplate) {
        this.reactiveRedisTemplate = reactiveRedisTemplate;

    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 白名单检查
        if (shouldSkip(exchange.getRequest())) {
            return chain.filter(exchange);
        }
        // 检查 Authorization 头
        String authHeader = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (authHeader == null || !authHeader.startsWith(tokenPrefix + " ")) {
            return unauthorizedResponse(exchange, "Missing authorization header");
        }
        // 提取 token
        String token = authHeader.substring(tokenPrefix.length() + 1);
        // 校验 token 并处理逻辑
        return validateToken(token)
                .doOnNext(valid -> log.info("Token validation result: {}", valid)) // 记录校验结果
                .onErrorResume(e -> {
                    log.error("Unexpected error in validateToken", e);
                    return Mono.just(false); // 降级为无效 token
                })
                .flatMap(valid -> {
                    if (!valid) {
                        log.warn("Token is invalid or revoked");
                        return unauthorizedResponse(exchange, "Token revoked");
                    }
                    log.info("Processing token...");
                    return processToken(exchange, chain, token)
                            .onErrorResume(e -> {
                                log.error("Error in processToken", e);
                                return unauthorizedResponse(exchange, "Authentication failed");
                            });
                })
                .onErrorResume(ExpiredJwtException.class, e -> {
                    log.warn("Token expired");
                    return unauthorizedResponse(exchange, "Token expired");
                })
                .onErrorResume(SignatureException.class, e -> {
                    log.warn("Invalid token signature");
                    return forbiddenResponse(exchange, "Invalid token signature");
                })
                .onErrorResume(Exception.class, e -> {
                    log.error("Unexpected error in filter", e);
                    return unauthorizedResponse(exchange, "Authentication failed");
                });
    }

    private Mono<Void> processToken(ServerWebExchange exchange,
                                    GatewayFilterChain chain,
                                    String token) {
        try {
            Claims claims = JWTUtil.extractAllClaims(token);
            List<SimpleGrantedAuthority> authorities = getAuthorities(claims);

            // 构建SecurityContext
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(
                            claims.getSubject(),
                            null,
                            authorities
                    );

            // 添加自定义请求头
            ServerHttpRequest mutatedRequest = exchange.getRequest().mutate()
                    .header("X-User-Id", claims.getSubject())
                    .header("X-User-Roles", String.join(",", getRoles(claims)))
                    .build();

            return chain.filter(exchange.mutate().request(mutatedRequest).build())
                    .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
        } catch (Exception e) {
            return Mono.error(e);
        }
    }

    private Mono<Boolean> validateToken(String token) {
        if (!enableRedisCheck) {
            return Mono.just(true);
        }
        return reactiveRedisTemplate.hasKey("revoked_tokens:" + token)
                .map(exists -> !exists)
                .onErrorResume(e -> {
                    log.error("Redis check failed for token: {}", token, e);
                    return Mono.just(true); // 降级为通过校验
                });
    }

    private List<SimpleGrantedAuthority> getAuthorities(Claims claims) {
        return getRoles(claims).stream()
                .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                .collect(Collectors.toList());
    }

    private List<String> getRoles(Claims claims) {
        Object rolesClaim = claims.get("roles");
        if (rolesClaim instanceof List) {
            return ((List<?>) rolesClaim).stream()
                    .filter(String.class::isInstance)
                    .map(String.class::cast)
                    .collect(Collectors.toList());
        } else if (rolesClaim instanceof String) {
            return Collections.singletonList((String) rolesClaim);
        }
        return Collections.emptyList();
    }

    private boolean shouldSkip(ServerHttpRequest request) {
        String path = request.getPath().value();
        return path.startsWith("/api/auth/") || path.equals("/health");
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String message) {
        return buildErrorResponse(exchange, HttpStatus.UNAUTHORIZED, message);
    }

    private Mono<Void> forbiddenResponse(ServerWebExchange exchange, String message) {
        return buildErrorResponse(exchange, HttpStatus.FORBIDDEN, message);
    }

    private Mono<Void> buildErrorResponse(ServerWebExchange exchange,
                                          HttpStatus status,
                                          String message) {
        exchange.getResponse().setStatusCode(status);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

        String jsonBody = String.format("{\"code\": %d, \"message\": \"%s\"}",
                status.value(), message);

        DataBuffer buffer = exchange.getResponse()
                .bufferFactory()
                .wrap(jsonBody.getBytes(StandardCharsets.UTF_8));

        return exchange.getResponse().writeWith(Mono.just(buffer));
    }

    @Override
    public int getOrder() {
        return -100; // 高优先级过滤器
    }
}